#pragma once

#include <list>
#include <boost/shared_ptr.hpp>
#include "cache.h"
#include "mswin/shared_resource.h"
#include "mswin/event.h"
#include "imglib/imageclass.h"

namespace icache {

class mem_cache : public cache, private mswin::shared_resource {
public:
	mem_cache(size_t quota = 0);
	~mem_cache() { }
	void quota(size_t q);
public:
	size_t quota() const;
	size_t size() const; ///< summed size of images in cache
	int items(); ///< the number of images currently in the cache
	bool empty() const;
public: //cache interface
	virtual void put(cached_image_sptr const & c, last_used_position p = most_recently_used);
	virtual cached_image_sptr get(item_id_sptr const & id, last_used_position p = most_recently_used);
	virtual cached_image_sptr try_get(item_id_sptr const & id, last_used_position p = most_recently_used);
	virtual bool touch(item_id_sptr const & id, last_used_position p = most_recently_used);
	virtual void remove(item_id_sptr const & id);
	virtual void purge();
private:
	friend struct equal_item_id;
	typedef std::list<cached_image_sptr> lru_type; ///< least recently used are in front of list, most recently used are at back
	typedef std::list<item_id_sptr> items_in_use_type;
private: //the following functions should be called only than the cache is locked
	void enforce_quota();
	void reposition_element(lru_type::iterator i, last_used_position p);
	void wait_while_loading(item_id_sptr const & id, auto_lock & l);
private:
	class image_loading {
	public:
		image_loading(mem_cache & mc, item_id_sptr const & id, last_used_position new_lru_pos);
		image_loading(mem_cache & mc, cached_image_sptr const & ci, last_used_position new_lru_pos);
		///if it's new image for cache, then size of cache is changed
		~image_loading();
		///return true if image was not found in cache, new cached_image was created and it is blocked for loading from other threads;
		///return false if image was in cache before object's contruction, therefore it's not blocked
		bool is_loading() const { return new_image; }
		cached_image_sptr const & e() { return *i; }
	public:
		last_used_position lru_pos;
	private:
		void init(cached_image_sptr const & ci);
	private:
		mem_cache & c;
		bool new_image;
		size_t old_size;
		lru_type::iterator i;
		items_in_use_type::iterator j;
	};
private:
	size_t _quota;
	size_t _size; ///< total size of elements in lru
	lru_type lru;
private:
	items_in_use_type loading_items;
	mswin::event image_loaded; ///< event pulses each time when an image is loaded in the cache
};

//inlines

inline size_t mem_cache::quota() const
	{ return _quota; }

inline size_t mem_cache::size() const
	{ return _size; }

inline bool mem_cache::empty() const
	{ return _size == 0; }

} //namespace icache
